﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using Apache.Cassandra;
using Cassandraemon;
using Cassandraemon.Serialization;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Cassandraemon.Test
{
    [TestClass]
    public class CassandraSerializerTest
    {
        public class SerializerTestClass
        {
            public byte u8 { get; set; }
            public int i32 { get; set; }
            public long i64 { get; set; }
            public double f64 { get; set; }
            public string str { get; set; }
        }

        [TestMethod]
        public void SerializerCacheTest()
        {
            var s1 = CassandraSerializer.GetSerializer<SerializerTestClass>();
            var s2 = CassandraSerializer.GetSerializer<SerializerTestClass>();
            Assert.ReferenceEquals(s1, s2);
        }

        [TestMethod]
        public void ToColumnsTest()
        {
            var obj = new SerializerTestClass()
            {
                u8 = 123,
                i32 = 123,
                i64 = -123,
                f64 = 987,
                str = "hello",
            };
            var columns = CassandraSerializer.ToColumns(obj);
            Assert.AreEqual(123, columns[0].Value[0]);
            Assert.AreEqual(123, columns[1].Value.ToInt32());
            Assert.AreEqual(-123, columns[2].Value.ToInt64());
            Assert.AreEqual((double)987, columns[3].Value.ToDouble());
            Assert.AreEqual("hello", columns[4].Value.ToUTF8());
            Assert.AreEqual(5, columns.Count);
        }

        /// <summary>
        /// Null should be ignored, or it will result in InvalidRequestException(Why: Column value is required).
        /// http://cassandraemon.codeplex.com/workitem/36
        /// </summary>
        [TestMethod]
        public void ToColumnsNullTest()
        {
            var obj = new SerializerTestClass();
            var columns = CassandraSerializer.ToColumns(obj);
            Assert.IsFalse(columns.Any(c => c.Name.SequenceEqual("str".ToCassandraByte())));
            Assert.AreEqual(4, columns.Count);
        }

        [TestMethod]
        public void ToObjectTest()
        {
            var columns = new List<Column>()
            {
                new Column { Name = "u8".ToCassandraByte(), Value = new byte[] { 123 } },
                new Column { Name = "i32".ToCassandraByte(), Value = 123.ToCassandraByte() },
                new Column { Name = "i64".ToCassandraByte(), Value = (-123L).ToCassandraByte() },
                new Column { Name = "f64".ToCassandraByte(), Value = (987.6).ToCassandraByte() },
                new Column { Name = "str".ToCassandraByte(), Value = "hello".ToCassandraByte() },
            };
            var obj = CassandraSerializer.ToObject<SerializerTestClass>(columns);
            Assert.AreEqual(123, obj.u8);
            Assert.AreEqual(123, obj.i32);
            Assert.AreEqual(-123L, obj.i64);
            Assert.AreEqual(987.6, obj.f64);
            Assert.AreEqual("hello", obj.str);
        }

        public class PrivateSetterTestClass
        {
            public int i32 { get; private set; }
            public void SetI32(int value) { i32 = value; }
        }

        [TestMethod]
        public void PrivateSetterTest()
        {
            var obj = new PrivateSetterTestClass();
            obj.SetI32(123);
            var columns = CassandraSerializer.ToColumns(obj);
            Assert.AreEqual(1, columns.Count);
            Assert.AreEqual(123, columns[0].Value.ToInt32());

            obj = CassandraSerializer.ToObject<PrivateSetterTestClass>(columns);
            Assert.AreEqual(123, obj.i32);
        }

        enum byteEnum : byte { a, b, c };
        enum intEnum { a, b, c };
        enum longEnum : long { a, b, c };

        [TestMethod]
        public void EnumTypeCodeTest()
        {
            Assert.AreEqual(TypeCode.Byte, Type.GetTypeCode(typeof(byteEnum)));
            Assert.AreEqual(TypeCode.Int32, Type.GetTypeCode(typeof(intEnum)));
            Assert.AreEqual(TypeCode.Int64, Type.GetTypeCode(typeof(longEnum)));
        }

        public class IgnoreDataMemberTestClass
        {
            public int i32 { get; set; }
            [IgnoreDataMember]
            public int NotSerialized { get; set; }
        }

        [TestMethod]
        public void IgnoreDataMemberTest()
        {
            Type type = typeof(IgnoreDataMemberTestClass);
            var properties = type.GetProperties()
                .Where(p => !p.GetCustomAttributes(
                    typeof(IgnoreDataMemberAttribute), true).Any());
            var factory = CompiledCassandraSerializerFactory.Default;
            var serializer = factory.CreateSerializer(type, properties);
            factory.SetSerializer(type, serializer);

            var target = new IgnoreDataMemberTestClass { i32 = 123, NotSerialized = 234 };
            var columns = CassandraSerializer.ToColumns(target);
            Assert.AreEqual(1, columns.Count);

            var actual = CassandraSerializer.ToObject<IgnoreDataMemberTestClass>(columns);
            Assert.AreEqual(123, actual.i32);
            Assert.AreEqual(0, actual.NotSerialized);
        }
    }
}
